/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.ipti.br.tag.dao;

import org.ipti.br.tag.entity.Actor;
import org.ipti.br.tag.entity.Actor_;
import org.ipti.br.tag.entity.Human;
import org.ipti.br.tag.entity.Human_;
import org.ipti.br.tag.entity.Unity;
import org.ipti.br.tag.entity.Unity_;
import org.ipti.br.tag.entity.Organization_;
import org.ipti.br.tag.entity.Personage_;
import org.ipti.br.tag.entity.enums.PersonageType;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

/**
 *
 * @author israel
 */
@Stateless
public class UnityFacade extends AbstractFacade<Unity> {

    @PersistenceContext(unitName = "TAG2PU")
    private EntityManager em;

    @Override
    protected EntityManager getEntityManager() {
        return em;
    }

    public UnityFacade() {
        super(Unity.class);
    }

    @Override
    public void create(Unity unity) {
        getEntityManager().persist(unity);
    }

    @Override
    public void edit(Unity unity) {
        getEntityManager().merge(unity);
    }

    public List<Unity> getUnityChildren(Long unityID) {
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Unity> cq = cb.createQuery(Unity.class);
        Root<Unity> from = cq.from(Unity.class);
        Predicate predicate = cb.equal(from.get(Unity_.unityFather).get(Unity_.id), unityID);
        cq.orderBy(cb.asc(from.get(Unity_.name)));
        cq.where(cb.and(predicate));

        return getEntityManager().createQuery(cq).getResultList();
    }

    public List<Unity> getUnitiesPerOrganization(Long organizationID) {
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Unity> cq = cb.createQuery(Unity.class);
        Root<Unity> from = cq.from(Unity.class);
        Predicate predicate = cb.equal(from.get(Unity_.organization).get(Organization_.id), organizationID);
        cq.orderBy(cb.asc(from.get(Unity_.name)));
        cq.where(cb.and(predicate));

        return getEntityManager().createQuery(cq).getResultList();
    }

    public List<Unity> getUnitiesPerOrganizationUnityFather(Long organizationID, Long unityFatherID) {
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Unity> cq = cb.createQuery(Unity.class);
        Root<Unity> from = cq.from(Unity.class);
        Predicate predicate = cb.equal(from.get(Unity_.organization).get(Organization_.id), organizationID);
        Predicate predicate2 = cb.equal(from.get(Unity_.unityFather).get(Unity_.id), unityFatherID);
        cq.orderBy(cb.asc(from.get(Unity_.name)));
        cq.where(cb.and(predicate, predicate2));

        return getEntityManager().createQuery(cq).getResultList();
    }

    public List<Unity> searchUnityPerNameOrganization(String unityName, Long organizationID) {
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Unity> cq = cb.createQuery(Unity.class);
        Root<Unity> root = cq.from(Unity.class);
        Predicate predicate1 = cb.like(cb.upper(root.get(Unity_.name)), "%" + unityName.toUpperCase() + "%");
        Predicate predicate2 = cb.lessThanOrEqualTo(root.get(Unity_.activated), Calendar.getInstance());
        Predicate predicate3 = cb.equal(root.get(Unity_.organization).get(Organization_.id), organizationID);
        Predicate predicate4 = cb.greaterThanOrEqualTo(root.get(Unity_.deactivated), Calendar.getInstance());
        Predicate predicate5 = cb.isNull(root.get(Unity_.deactivated));

        cq.where(cb.and(predicate1, predicate2, predicate3), cb.or(predicate4, predicate5));
        cq.orderBy(cb.asc(root.get(Unity_.name)));

        return getEntityManager().createQuery(cq).getResultList();
    }

    public List<Unity> searchUnityPerOrganizationYear(Long organizationID, int year) {
        Calendar calendarInitial = new GregorianCalendar(year, 1, 1);
        Calendar calendarEnd = new GregorianCalendar(year, 12, 31);
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Unity> cq = cb.createQuery(Unity.class);
        Root<Unity> from = cq.from(Unity.class);
        Predicate predicate1 = cb.equal(from.get(Unity_.organization).get(Organization_.id), organizationID);
        Predicate predicate2 = cb.greaterThanOrEqualTo(from.get(Unity_.activated), calendarInitial);
        Predicate predicate3 = cb.lessThanOrEqualTo(from.get(Unity_.deactivated), calendarEnd);
        cq.orderBy(cb.asc(from.get(Unity_.name)));
        cq.where(cb.and(predicate1, predicate2, predicate3));

        return getEntityManager().createQuery(cq).getResultList();
    }

    public List<Unity> modifiedUnitiesBetweenDate(Calendar initialDate, Calendar finalDate, Long serialNumber) {
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Unity> cq = cb.createQuery(Unity.class);
        Root<Unity> from = cq.from(Unity.class);
        Predicate predicate1 = cb.notEqual(from.get(Unity_.serialNumber), serialNumber);
        Predicate predicate2 = cb.greaterThanOrEqualTo(from.get(Unity_.lastModified), initialDate);
        Predicate predicate3 = cb.lessThan(from.get(Unity_.lastModified), finalDate);
        cq.where(cb.and(predicate1, predicate2, predicate3));

        return getEntityManager().createQuery(cq).getResultList();
    }

    public List<Unity> modifiedUnitiesBetweenDateWithFather(Calendar initialDate, Calendar finalDate, Long serialNumber) {
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Unity> cq = cb.createQuery(Unity.class);
        Root<Unity> from = cq.from(Unity.class);
        Predicate predicate1 = cb.notEqual(from.get(Unity_.serialNumber), serialNumber);
        Predicate predicate2 = cb.greaterThanOrEqualTo(from.get(Unity_.lastModified), initialDate);
        Predicate predicate3 = cb.lessThan(from.get(Unity_.lastModified), finalDate);
        Predicate predicate4 = cb.isNotNull(from.get(Unity_.unityFather));
        cq.where(cb.and(predicate1, predicate2, predicate3, predicate4));

        return getEntityManager().createQuery(cq).getResultList();
    }

    public List<Unity> findAllOrder() {
        return findAllOrderBy(Unity_.name);
    }
    
    public List<Unity> activatedUnities(List<Long> unities) {
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Unity> cq = cb.createQuery(Unity.class);
        Root<Unity> root = cq.from(Unity.class);
        
        List<Predicate> unitiesPredicateList = new ArrayList<Predicate>();
        for (Long l : unities) {
            unitiesPredicateList.add(cb.equal(root.get(Unity_.id), l));
        }
        
        if (unitiesPredicateList.isEmpty()) {
            return new ArrayList<Unity>();
        } else if (unitiesPredicateList.size() == 1) {
            cq.where(
                    cb.and(cb.lessThanOrEqualTo(root.get(Unity_.activated), Calendar.getInstance())),
                    cb.or(
                    cb.greaterThanOrEqualTo(root.get(Unity_.deactivated), Calendar.getInstance()),
                    cb.isNull(root.get(Unity_.deactivated))),
                    unitiesPredicateList.get(0));
        } else {
            cq.where(cb.and(cb.lessThanOrEqualTo(root.get(Unity_.activated), Calendar.getInstance())),
                    cb.or(
                        cb.greaterThanOrEqualTo(root.get(Unity_.deactivated), Calendar.getInstance()),
                        cb.isNull(root.get(Unity_.deactivated))),
                    cb.or(unitiesPredicateList.toArray(new Predicate[0])));
        }
        
        cq.orderBy(cb.asc(root.get(Unity_.name)));
        Query q = getEntityManager().createQuery(cq);
        return q.getResultList();
    }
    
    public List<Unity> activatedUnitiesPerID(List<Long> unities,
            int first, int pageSize, Map<String, String> filters) {
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Unity> cq = cb.createQuery(Unity.class);
        Root<Unity> root = cq.from(Unity.class);
        
        List<Predicate> unitiesPredicateList = new ArrayList<Predicate>();
        for (Long l : unities) {
            unitiesPredicateList.add(cb.equal(root.get(Unity_.id), l));
        }
        
        List<Predicate> predicateList = new ArrayList<Predicate>();
        predicateList.add(cb.lessThanOrEqualTo(root.get(Unity_.activated), Calendar.getInstance()));
        
        for (String filter : filters.keySet()) {
            Predicate p = null;
            if(filter.equals("name")) {
                p = cb.like(cb.upper(root.get(Unity_.name)), "%" + filters.get(filter).toUpperCase() + "%");
            }
            
            if(p != null) {
                predicateList.add(p);
            }
        }
        
        if (unitiesPredicateList.isEmpty()) {
            return new ArrayList<Unity>();
        } else if (unitiesPredicateList.size() == 1) {
            cq.where(
                    cb.and(predicateList.toArray(new Predicate[0])),
                    cb.or(
                    cb.greaterThanOrEqualTo(root.get(Unity_.deactivated), Calendar.getInstance()),
                    cb.isNull(root.get(Unity_.deactivated))),
                    unitiesPredicateList.get(0));
        } else {
            cq.where(cb.and(predicateList.toArray(new Predicate[0])),
                    cb.or(
                        cb.greaterThanOrEqualTo(root.get(Unity_.deactivated), Calendar.getInstance()),
                        cb.isNull(root.get(Unity_.deactivated))),
                    cb.or(unitiesPredicateList.toArray(new Predicate[0])));
        }
        
        cq.orderBy(cb.asc(root.get(Unity_.name)));
        Query q = getEntityManager().createQuery(cq);
        q.setFirstResult(first);
        q.setMaxResults(pageSize);
        return q.getResultList();
    }
    
    public int getUnityCount(List<Long> unities) {
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery cq = cb.createQuery();
        Root<Unity> root = cq.from(Unity.class);
        cq.select(cb.count(root));
        
        List<Predicate> unitiesPredicateList = new ArrayList<Predicate>();
        for (Long l : unities) {
            unitiesPredicateList.add(cb.equal(root.get(Unity_.id), l));
        }
        
        List<Predicate> predicateList = new ArrayList<Predicate>();
        predicateList.add(cb.lessThanOrEqualTo(root.get(Unity_.activated), Calendar.getInstance()));
                
        if (unitiesPredicateList.isEmpty()) {
            return 0;
        } else if (unitiesPredicateList.size() == 1) {
            cq.where(
                    cb.and(predicateList.toArray(new Predicate[0])),
                    cb.or(
                    cb.greaterThanOrEqualTo(root.get(Unity_.deactivated), Calendar.getInstance()),
                    cb.isNull(root.get(Unity_.deactivated))),
                    unitiesPredicateList.get(0));
        } else {
            cq.where(cb.and(predicateList.toArray(new Predicate[0])),
                    cb.or(
                        cb.greaterThanOrEqualTo(root.get(Unity_.deactivated), Calendar.getInstance()),
                        cb.isNull(root.get(Unity_.deactivated))),
                    cb.or(unitiesPredicateList.toArray(new Predicate[0])));
        }        
        
        return ((Long) getEntityManager().createQuery(cq).getSingleResult()).intValue();
    }
    
    public List<Unity> unitiesPerID(List<Long> unities) {
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Unity> cq = cb.createQuery(Unity.class);
        Root<Unity> root = cq.from(Unity.class);
        
        List<Predicate> unitiesPredicateList = new ArrayList<Predicate>();
        for (Long l : unities) {
            unitiesPredicateList.add(cb.equal(root.get(Unity_.id), l));
        }       
        
        if (unitiesPredicateList.isEmpty()) {
            return new ArrayList<Unity>();
        } else if (unitiesPredicateList.size() == 1) {
            cq.where(unitiesPredicateList.get(0));
        } else {
            cq.where(cb.or(unitiesPredicateList.toArray(new Predicate[0])));
        }
        
        cq.orderBy(cb.asc(root.get(Unity_.name)));
        Query q = getEntityManager().createQuery(cq);
        return q.getResultList();
    }
    
    public Unity getSchool(Unity unityFather){
                
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Unity> cq = cb.createQuery(Unity.class);
        Root<Unity> root = cq.from(Unity.class);
        Predicate predicate1 = cb.equal( root.join(Unity_.organization).get(Organization_.name), "Escola");
        Predicate predicate2 = cb.equal( root.get(Unity_.unityFather), unityFather);
        
        cq.where( predicate1, predicate2);
                
        try{
            return getEntityManager().createQuery(cq).getSingleResult();
            
        }catch(NoResultException e){
            return null;
        }
    }
    
    public List<Actor> getStudentsBySchool(Unity unity){
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Actor> cq = cb.createQuery(Actor.class);
        Root<Actor> root = cq.from(Actor.class);
        Predicate predicate1 = cb.equal(root.get(Actor_.unity), unity);
        Predicate predicate2 = cb.equal(root.join(Actor_.personage).get(Personage_.personageType),PersonageType.MATRICULATED);
        Predicate predicate3 = cb.or(
                    cb.greaterThanOrEqualTo(root.get(Actor_.deactivated), Calendar.getInstance()),
                    cb.isNull(root.get(Actor_.deactivated))
                );
        cq.where (predicate1,predicate2,predicate3);
        try{
            return getEntityManager().createQuery(cq).getResultList();
            
        }catch(NoResultException e){
            return null;
        }
    }
    
    //Procura unidade pelo Hash do objeto
    public Unity find(Unity unity) {
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Unity> cq = cb.createQuery(Unity.class);
        Root<Unity> root = cq.from(Unity.class);
        unity.generateHash();
        Integer hash = unity.hashCode();
        
        Predicate p1 = cb.equal( root.get(Unity_.id), hash);
        
        cq.where(p1);
        try{
            return getEntityManager().createQuery(cq).getSingleResult();
            
        }catch(NoResultException e){
            return null;
        }
    }
    
}
